Building your first project
Creating your project
To create a new Uno app, there are two options available to developers. The first is to use Uno.Templates by invoking the dotnet new
command, which provides a quick and straightforward way to get started with an Uno project. The second option is to use the Uno Platform Template Wizard, which provides a more guided approach to creating an Uno app.
In the following sections, we will cover both methods for creating a new Uno app, providing step-by-step instructions for each.
For this project, we will be using the unoapp
solution template. This template includes two preset configurations, blank
and recommended
, and a large number of other options that can be used to define the structure of the app to be created by the template.
For this project, we're going to select the blank
preset, which only includes the minimum features required for an Uno Platform application. We're also going to select the Material theme and include the Uno Toolkit for the helper functions to switch themes.
As we're using the MVVM pattern, let's specify the -presentation mvvm
option. Additionally, since we're using C# Markup, let's specify -markup csharp
.
dotnet new unoapp -preset blank -tfm net8.0 -markup csharp -presentation mvvm -toolkit true -theme material -theme-service -o SimpleCalculator
Note
After creating the SimpleCalculator application, open the Solution File (Visual Studio) or Solution Folder (Visual Studio Code) and ensure you can build and run the application.
Prepare the Project
Before we continue to the next section, add the Calculator class by adding a new file called Calculator.cs into the SimpleCalculator project. Replace the default Calculator
class with the following source code.
Calculator.cs code contents (collapsed for brevity)
using System.Globalization;
namespace SimpleCalculator;
public record Calculator
{
private string? Number { get; init; }
private string? Operator { get; init; }
private double? Number1 { get; init; }
private double? Number2 { get; init; }
private bool IsNumber2Percentage { get; init; }
private double? Result { get; init; }
private bool HasOperator => !string.IsNullOrEmpty(Operator);
private bool HasNumber => !string.IsNullOrEmpty(Number);
private bool HasNumber1 => Number1 != null;
public string Output => $"{(Result != null ? Result.Value.ToString(CultureInfo.InvariantCulture) : HasNumber ? Number : "0")}";
public string Equation => $"{Number1?.ToString(CultureInfo.InvariantCulture)} {Operator} {Number2?.ToString(CultureInfo.InvariantCulture)}{(IsNumber2Percentage ? "%" : string.Empty)}{(Result != null ? $" =" : string.Empty)}";
public Calculator Input(string key)
{
var calculator = RestartOrClear(key);
return key switch
{
"÷" or "×" or "+" or "−" => OperatorKey(calculator, key),
"back" => BackKey(calculator),
"." => DotKey(calculator),
"0" when !calculator.HasNumber => calculator,
"C" => new(),
"=" => EqualsKey(calculator),
"%" => PercentageKey(calculator),
"±" => PlusMinusKey(calculator),
_ => calculator with { Number = calculator.Number + key }
};
}
private Calculator RestartOrClear(string key)
{
if (Result != null)
{
if (key == "÷" || key == "×" || key == "+" || key == "−")
{
return this with
{
Number1 = Result,
Result = null,
Number2 = null,
Number = null,
Operator = key,
IsNumber2Percentage = false
};
}
else
{
return new();
}
}
return this;
}
private static Calculator BackKey(Calculator calculator)
{
if (calculator.HasNumber)
{
calculator = calculator with
{
Number = calculator.Number?.Substring(0, calculator.Number.Length - 1)
};
}
return calculator;
}
private static Calculator DotKey(Calculator calculator)
{
if (calculator.HasNumber)
{
if (calculator.Number?.Contains(".") == false)
{
calculator = calculator with
{
Number = calculator.Number + "."
};
}
}
else
{
calculator = calculator with
{
Number = "0."
};
}
return calculator;
}
private static Calculator EqualsKey(Calculator calculator)
{
if (calculator.HasOperator && calculator.HasNumber1)
{
double? number2 = calculator.HasNumber ? GetNumber(calculator.Number) : 0.0;
double result = calculator.Operator switch
{
"÷" => calculator.Number1!.Value / number2!.Value,
"×" => calculator.Number1!.Value * number2!.Value,
"+" => calculator.Number1!.Value + number2!.Value,
"−" => calculator.Number1!.Value - number2!.Value,
_ => throw new InvalidOperationException()
};
calculator = calculator with
{
Number2 = number2,
Result = result
};
}
return calculator;
}
private static Calculator PercentageKey(Calculator calculator)
{
if (calculator.HasOperator && calculator.HasNumber1)
{
double? number2 = calculator.HasNumber ? GetNumber(calculator.Number) : 0.0;
double result = calculator.Operator switch
{
"÷" => calculator.Number1!.Value / (number2!.Value / 100) * calculator.Number1!.Value,
"×" => calculator.Number1!.Value * (number2!.Value / 100) * calculator.Number1!.Value,
"+" => calculator.Number1!.Value + (number2!.Value / 100) * calculator.Number1!.Value,
"−" => calculator.Number1!.Value - (number2!.Value / 100) * calculator.Number1!.Value,
_ => throw new InvalidOperationException()
};
calculator = calculator with
{
Number2 = number2,
Result = result,
IsNumber2Percentage = true
};
}
return calculator;
}
private static Calculator PlusMinusKey(Calculator calculator)
{
if (calculator.HasNumber)
{
calculator = calculator with { Number = calculator.Number?.StartsWith("-") == true ? calculator.Number?.Substring(1) : "-" + calculator.Number };
}
return calculator;
}
private static Calculator OperatorKey(Calculator calculator, string key)
{
if(calculator.HasNumber)
{
if(calculator.HasOperator && calculator.HasNumber1)
{
calculator = EqualsKey(calculator);
calculator = calculator with
{
Operator = key,
Number1 = calculator.Result,
Number = null,
Number2 = null,
Result = null
};
}
else if (!calculator.HasOperator)
{
calculator = calculator with
{
Operator = key,
Number1 = GetNumber(calculator.Number),
Number = null
};
}
}
else if (calculator.HasOperator && calculator.HasNumber1)
{
calculator = calculator with
{
Operator = key
};
}
return calculator;
}
private static double? GetNumber(string? number)
=> Convert.ToDouble(number, CultureInfo.InvariantCulture);
}
Let's also create a temporary class to serve as a placeholder for the DataContext. We'll discuss this further in the Architecture module. Add the following TempDataContext
class:
public class TempDataContext
{
public bool IsDark { get; set; }
public ICommand? InputCommand { get; set; }
public Calculator Calculator { get; } = new Calculator();
}
Additional Resources
Next Steps
Next, we'll add the design to our Simple Calculator. You can choose to bring in the UI from the Uno Figma Plugin and import it, or you can directly add the code to the project without exporting it.